#include "core_pch.h"
#include "data.h"
#include "DString.h"
#include "Utility.h"


struct StrData 
{
	s32_t RefCount;
	u16_t Cap;
	u16_t Len;

	char Data[32];

	s32_t UpdateLen()
	{
		Len = strlen( Data);
		return Len;
	}
};

const u32_t CHandleOffset = sizeof( s32_t)+sizeof( u16_t)+sizeof( u16_t);

s32_t __strDataNum = 0;

s32_t __reportStrDataNum()
{
	return __strDataNum;
}

//////////////////Data Utility//////////////////
StrData* __allocateStrData( u32_t num)
{
	if (num == 0)
		num = 1;

	u32_t bufSize = num * sizeof(char) + CHandleOffset + 
		sizeof( char);

	if ( bufSize < 32)
		bufSize = 32;
	else if ( bufSize < 64)
		bufSize = 64;
	else if ( bufSize < 128)
		bufSize = 128;
	else if ( bufSize < 128*2)
		bufSize = 128*2;
	else if ( bufSize < 128*3)
		bufSize = 128*3;
	else if ( bufSize < 128*4)
		bufSize = 128*4;
	else if ( bufSize < 128*6)
		bufSize = 128*6;
	else if ( bufSize < 1024)
		bufSize = 1024;
	else 
		++bufSize;

	StrData* rst = (StrData*)coreAlloc( bufSize);
	if( rst != null_ptr)
	{
		rst->Cap = bufSize - CHandleOffset;
		rst->Len = 0;
		rst->RefCount = 1;
		rst->Data[0] = '\0';
		++__strDataNum;
	}

	return rst;
}

StrData* __allocateCopyStrData(const char* srcStr)
{
    if ( srcStr == null_ptr)
        return null_ptr;

    u32_t stLength = strlen( srcStr);
    StrData* rst = __allocateStrData( stLength);

    if ( rst != null_ptr)
	{
		coreMemcpy( rst->Data, srcStr, stLength + 1);

		_check( strcmp( rst->Data, srcStr) == 0);
		rst->Len = stLength;
	}

	return rst;
}

StrData* __allocateCopyStrData( StrData* data)
{
	if( data == null_ptr)
	{
		return null_ptr;
	}

	s32_t strLen = data->Len;
    StrData* rst = __allocateStrData( strLen);

    if ( rst != null_ptr)
	{
		coreMemcpy( rst->Data, data->Data, strLen + 1);
		_check( strcmp( rst->Data, data->Data) == 0);
		rst->Len = strLen;
	}

	return rst;
}

void __incRefStrData( StrData*& strData)
{
	if( strData == null_ptr)
	{
		return;
	}

	
	coreAtomicInc( strData->RefCount);

}

void __decRefStrData( StrData*& strData)
{
	if( strData == null_ptr)
	{
		return;
	}

	coreAtomicDec( strData->RefCount);

	_check( strData->RefCount >= 0);
	if (strData->RefCount == 0)
	{
		--__strDataNum;
		coreFree( strData);
	}

	strData = null_ptr;
}

s32_t __refCountStrData( StrData* data)
{
    if ( !data)
    {
        return 0;
    }
    else
    {
		return data->RefCount;
    }
}

int __vsnPrintf( char* dst, size_t dstSize,
    const char* fmt, va_list args)
{
	if( dstSize == 0)
	{
		return 0;
	}

	_check( dst);
	_check( fmt);

	// make dst empty first
    dst[0] = '\0';

    s32_t count = dstSize - 1;
	int rst = vsnprintf( dst, count, fmt, args);

	if ( rst == -1)
    {
        dst[ count] = '\0';
    }
    else
    {
        dst[ rst] = '\0';
    }

	return rst;
}

StrData* __formatStrData( StrData* inData, const char* fmt, va_list argPtr)
{
	_check( __refCountStrData( inData) < 2);
	s32_t bufSize = 0;
	if( inData)
	{
		bufSize = inData->Cap;
	}

    int numChars = -1;
	if ( bufSize != 0)
    {
		numChars = __vsnPrintf( inData->Data, bufSize, fmt, argPtr);
    }

    while (numChars < 0 || inData == null_ptr ||
        numChars == bufSize - 1)
    {
		__decRefStrData( inData);
		inData = __allocateStrData( s32_t( bufSize*1.5f) + 32);
		bufSize = inData->Cap;

		numChars = __vsnPrintf( inData->Data, bufSize, fmt, argPtr);
    }

	inData->Data[numChars] = '\0';
	inData->Len = numChars;

    return inData;
}

////////////////////// DString //////////////////////
DString::DString( u32_t num)
{
	if( num)
	{
		mData = __allocateStrData( num);
		mData->Data[0] = '\0';
		mData->UpdateLen();
	}
	else
	{
		mData = null_ptr;
	}
}

DString::DString( const char* c_str)
{
    mData = __allocateCopyStrData(c_str);
}

DString::DString( const DString& other)
{
	mData = other.mData;
	__incRefStrData( mData );
}

DString::~DString()
{
	__decRefStrData( mData);
}

s32_t DString::Num() const
{
	if( !mData)
	{
		return 0;
	}

	return mData->Len;
}

// return pointer to const string
DString::operator const char*() const
{
	if( !mData)
	{
		return null_ptr;
	}

	return mData->Data;
}

char const& DString::operator[](s32_t index)
{
	_check( index < Num());
	return mData->Data[index];
}


void DString::FormatSelf(const char* fmt, ...)
{
	// make sure don't format self
    StrData* dataCopy = mData;
    __incRefStrData( dataCopy);
	_makeInstanceData( true);
	_check( mData != dataCopy || mData == null_ptr);

    va_list argList;
    va_start(argList, fmt);
    mData = __formatStrData( mData, fmt, argList);
    va_end(argList);

    __decRefStrData( dataCopy);
}

DString DString::Format( const char* fmt, ...)
{

	DString ret;
    va_list argList;
    va_start(argList, fmt);
	ret.mData = __formatStrData( 0, fmt, argList);
    va_end(argList);

	return ret;
}

void DString::_makeInstanceData( bool force)
{
    if(__refCountStrData( mData) > 1 || force)
	{
		StrData* data = mData;
        mData = __allocateCopyStrData( data);
		__decRefStrData( data);
	}
}

void DString::_makeInstanceDataAddMakeRoom( s32_t num, bool force)
{
	if( _makeRoom( num))
	{
		return;
	}

	_makeInstanceData( force);
}

bool DString::_makeRoom( s32_t num)
{
	s32_t newStringLength = Num() + num;
	u32_t currBufSize = 0;
	if( mData)
	{
		currBufSize = mData->Cap;
	}

	if ( newStringLength + sizeof(char) > currBufSize)
	{
		StrData* newData = __allocateStrData( newStringLength);
		s32_t end = Num();
		coreMemcpy( newData->Data, mData->Data, end);
		newData->Data[end] =  '\0';
		newData->Len = end;
		__decRefStrData( mData);
		mData = newData;
		return true;
	}
	else
	{
		return false;
	}
}

int DString::Cmp(const char* other) const
{ 
    if (other == null_ptr && mData ==  null_ptr)
        return 0;
    else if ( other == NULL)
		return mData->Data[0];
    else if (mData == null_ptr)
        return -other[0];
    return strcmp( mData->Data, other);
}

int DString::ICmp(const char* other) const
{ 
    if (other == null_ptr && mData ==  null_ptr)
        return 0;
    else if ( other == NULL)
		return mData->Data[0];
    else if (mData == null_ptr)
        return -other[0];
    return stricmp( mData->Data, other);
}

DString DString::Sub( s32_t bgn, s32_t ed) const
{
	_check( bgn < ed);
	_check( bgn < Num());
	_check( ed <= Num());
	if ( mData != null_ptr)
    {
        DString rst((size_t)(ed - bgn + 2));
		coreStrncpy( rst.mData->Data, rst.mData->Cap -1, mData->Data + bgn,  ed - bgn);
		rst.mData->Data[ed - bgn] = '\0';
		rst.mData->Len = ed - bgn;
        return rst;
    }
    else
    {
        return DString();
    }
}


DString DString::Split( const char* delimeters, s32_t& begin) const
{
	_check( begin >=0 );
	_check( delimeters);

	if (begin < Num() && mData != null_ptr)
	{
		char* str = strpbrk( mData->Data + begin, delimeters);

		if ( str == null_ptr)
		{
			s32_t ob = begin;
			begin = Num();
			return Sub( ob, Num());
		}
		else
		{
			s32_t splPos = str - mData->Data;
			if( splPos == begin)
			{
				++begin;
				return Split( delimeters, begin);
			}

			s32_t oldBegin = begin;
			begin = splPos;
			return Sub( oldBegin, splPos);
		}
	}
	else
	{
		return DString();
	}
}



s32_t DString::Find(const char* str, s32_t start) const
{ 
    if ( start >= Num())
		return null_index;
    if ( str == null_ptr || str[0] == '\0')
        return null_index;
	char* subStr = strstr( mData->Data + start, str);
    if (subStr == null_ptr)
        return null_index;

	s32_t rst = subStr - mData->Data;
    if ( rst < 0)
        return null_index;
    else
        return rst;
}

s32_t DString::FindFristOf(const char* findVal, s32_t begin) const
{
	_check( begin >=0 );
	_check( findVal);

	if(  mData != null_ptr)
	{
		char* str = strpbrk( mData->Data + begin, findVal);
		if ( str != null_ptr)
		{
			return str - mData->Data;
		}
	}
	return null_index;

}

void DString::Replace(const char* toReplace , const char* replaceWith)
{
	if (toReplace == null_ptr || replaceWith == null_ptr)
		return;

	s32_t oldStrLen = strlen(toReplace);
	s32_t newStrLen = strlen(replaceWith);
	s32_t count = 0;
	s32_t orgLen = Num();

	s32_t i = 0;
	for (; i < orgLen && i != null_index;)
	{
		i = Find( toReplace, i);
		if (i != null_index)
		{
			count++;
			i += oldStrLen;
		}
	}

	if (count == 0)
		return;

	s32_t numCharsToReplace = count * oldStrLen;
	s32_t numCharsNeededToReplace = count * newStrLen;
	s32_t sizeNeeded = orgLen - numCharsToReplace + numCharsNeededToReplace;

	if ( sizeNeeded > orgLen)
		_makeInstanceDataAddMakeRoom( sizeNeeded - orgLen, false);
	else
		_makeInstanceData( false);

	for (i = 0; i < sizeNeeded && count > 0; count--)
	{
		char* pcStart = strstr(mData->Data + i, toReplace);

		_check(pcStart != null_ptr);
		char* pcEnd = pcStart + newStrLen;

		memmove(pcStart + newStrLen, pcStart + oldStrLen,
			strlen(pcStart + oldStrLen) + 1);
		memcpy(pcStart, replaceWith, newStrLen);
		i = pcEnd - mData->Data;
	}

	mData->Data[sizeNeeded] = '\0';
	mData->UpdateLen();
}

void DString::Upper()
{ 
    if ( mData == null_ptr)
        return;

    _makeInstanceData( false);
    for ( s32_t i = 0; i < Num() ; i++)
    {
		char& val = mData->Data[i];
		val = _toUpper( val);
    }
}

void DString::Lower()
{
    if ( mData == null_ptr)
        return;

    _makeInstanceData(false);
    for ( s32_t i = 0; i < Num() ; i++)
    {
		char& val = mData->Data[i];
		val = _toLower( val);
    }
}

void DString::Cat(char ch)
{ 
    s32_t num = Num();
	_makeInstanceDataAddMakeRoom(true, true);

	mData->Data[num] = ch;
    mData->Data[num+1] = '\0';
	mData->Len = num + 1;
}

void DString::Cat( const char* pcStr)
{

    if (pcStr == NULL)
        return;

    s32_t sLen = strlen(pcStr);
    if ( sLen > 0)
    {
        StrData* data = mData;
        bool incRefed = 0;
		if ( pcStr == data->Data)
        {
			__incRefStrData( data);

            _makeInstanceDataAddMakeRoom(sLen, true);
            incRefed = 1;
        }
        else
        {
            _makeInstanceDataAddMakeRoom(sLen, 0);
        }

		coreStrcpy( mData->Data + mData->Len, 
            sLen + 1, pcStr);

		mData->UpdateLen();

        if (incRefed)
		{
			__decRefStrData(data);
		}
    }
}

DString  operator+(const DString& string1, const DString& string2)
{
    DString str_(string1);
    str_.Cat(string2);
    return str_;
}

DString  operator+(const DString& string1, char* pcStr)
{
    DString str_(string1);
    str_.Cat(pcStr);
    return str_;
}

DString  operator+(char* pcStr, const DString& string1)
{
    DString str_(pcStr);
    str_.Cat(string1);
    return str_;
}

DString  operator+(const DString& string, char ch)
{
   DString str_(string);
   str_.Cat(ch);
   return str_;
}

DString  operator+(char ch, const DString& string)
{ 
   DString str_(ch);
   str_.Cat(string);
   return str_;
}

char* __chPtrStrData( StrData* data)
{
	if( data == 0)
	{
		return 0;
	}

	return data->Data;
}



void __strDataWrite( StrData*& data, const char* newStr )
{
    if (newStr == NULL)
    {
        __decRefStrData (data);
        return;
    }
    
    if (data == NULL)
    {
		data = __allocateCopyStrData( newStr);
        return;
    }
    

	coreAtomicDec( data->RefCount);

    if (data->RefCount == 0)
    {
        s32_t stLength = strlen(newStr);
		if ( data->Cap  >= (stLength + 1))
        {
            data->RefCount = 1;
        }
        else
        {
			__decRefStrData (data);
			data = __allocateStrData( stLength);
        }

		coreMemcpy( data->Data, newStr, stLength + 1);
		_check( strcmp(data->Data, newStr) == 0);
		data->Len = stLength;
    }
    else
    {
		data = __allocateCopyStrData( newStr);
    }
}


DString& DString::operator=(DString& stringSrc)
{
	if ( __chPtrStrData( stringSrc.mData) == __chPtrStrData( mData))
        return *this;

	
	__decRefStrData( mData);
	__incRefStrData( stringSrc.mData);
	mData = stringSrc.mData;

    return *this;
}

DString& DString::operator=(const char* lpsz)
{
    if (lpsz == __chPtrStrData(mData))
        return *this;

    __strDataWrite(mData, lpsz);
    return *this;
}

DString& DString::operator=(char ch)
{ 
    char acString[2];
    acString[0] = ch;
    acString[1] = '\0';
    return DString::operator=((const char*)&acString[0]);
}

DString& DString::operator=(char* lpsz)
{
    return DString::operator=((const char*)lpsz);
}


DString& DString::operator+=(char ch)
{
     Cat(ch);
     return *this;
}

DString& DString::operator+=(DString& stringSrc)
{
    Cat(stringSrc);
    return *this;
}

DString& DString::operator+=(char* lpsz)
{
     Cat(lpsz);
     return *this;
}

DString& DString::operator+=(const char* pcString)
{
    Cat(pcString);
    return *this;
}
